#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>

#include "debug.h"
#include "os_api.h"
#include "ps_api.h"
#include "dev_api.h"
#include "uart.h"
#include "dtu_mqtt.h"
#include "dtu_socket.h"

#define UPCASE( c ) ( ((c) >= 'a' && (c) <= 'z') ? ((c) - 0x20) : (c) )

#define MAX_ARG       32

#define MAX_LINE_SIZE 560

#define AT_CMD_NAME_MAX_LEN 16

typedef struct{
	int port;
    int len;
    UINT8 *data;
}uartMsgData_S;


#define TC_BUFFER_MAXSIZE	(2048+1)


#define UART_TASK_STACK_SIZE     1024*8
static void* uart_task_stack = NULL;
static OSTaskRef uart_task_ref = NULL;
static OSMsgQRef uart_msgq_ref = NULL;
static OSATimerRef uart_task_timer_ref = NULL;

static int dtu_echo = 0;
static int dtu_debug = 0;
/*--------------------------------------------------------------------------
                Syntax Flags For the AT Command Parser
--------------------------------------------------------------------------*/
#define NA    1                 /*  Name field found      */
#define EQ    2                 /*  <=> found             */
#define QU    4                 /*  <?> found             */
#define AR    8                 /*  Argument field found  */

typedef uint8_t     byte;

typedef enum {
    DSAT_DATA_MODE = 0,
    DSAT_AT_MODE,
	
    DSAT_MAX_MODE
} dsat_serila_mode_type;

static dsat_serila_mode_type serial_mode = DSAT_DATA_MODE;

typedef enum {
    DSAT_CMD_PREP_STATE_HUNT,
    DSAT_CMD_PREP_STATE_FOUND_A,
    DSAT_CMD_PREP_STATE_FOUND_AT,
    DSAT_CMD_PREP_STATE_ERROR
} dsat_cmd_prep_state_enum_type;

typedef struct dsat_sio_info_s {
    char cmd_line_buffer[MAX_LINE_SIZE];
    char *build_cmd_ptr;
    dsat_cmd_prep_state_enum_type  at_cmd_prep_state;
} dsat_sio_info_s_type;

static dsat_sio_info_s_type g_sio_info;

typedef enum {
    DSAT_OK             = 0,
    DSAT_NO_CARRIER     = 3,
    DSAT_ERROR          = 4,
} dsat_result_enum_type;


typedef struct {
    byte *arg[MAX_ARG];
    byte working_at_line[MAX_LINE_SIZE];
    byte *name;
    byte *end_of_line;
    uint8_t op;
    uint16_t args_found;
} tokens_struct_type;

typedef struct {
    int result;
    char *response;
} AtCmdResponse;

void send_string_to_uart(char *string);

int dsat_strip_quotes_out( const byte * quoted_str, byte * out_str, int str_len)
{
  int index;
  int end_quote_found = 0;

  if(quoted_str[0] != '"')
  {
    LOG_PRINTF("The in_str must be quoted");
    return -1;
  }
  
  for (index = 0; 
       (index < str_len) && (quoted_str[index + 1] != '\0') 
         && !end_quote_found; 
       index++)
  {
    if(quoted_str[index + 1] == '"')
    {
      end_quote_found = TRUE;
      out_str[index] = '\0'; 
    }
    else
    {
      out_str[index] = quoted_str[index + 1];
    }
  }

  if(!end_quote_found)
  {
    LOG_PRINTF("The in_str has opened quotes, or out_str is small: %d", str_len);
    return -1;
  }
  return 0;
}

void send_reset_cmd(void);

void send_close_uart_at_cmd(void);

void reboot_callback(UINT32 id)
{
	send_reset_cmd();
}

void dtu_uart_log(char *log)
{	
	// 当前串口打印调试信息
	if (dtu_debug){
		send_string_to_uart("\r\n***** ");
		send_string_to_uart(log);
		send_string_to_uart(" *****\r\n");		
	}else{
		LOG_PRINTF("%s", log);
	}
}

static int dtu_atcmd_echo(const tokens_struct_type *tok_ptr, AtCmdResponse * resp)
{
	dsat_result_enum_type result = DSAT_ERROR;

	if (tok_ptr->op == NA) {
		if (0 == strcasecmp((char *)tok_ptr->name,"E1")) {
			dtu_echo = 1;
			result = DSAT_OK;
		} else if (0 == strcasecmp((char *)tok_ptr->name,"E0")) {
			dtu_echo = 0;
			result = DSAT_OK;
		}
	}
	return result;
}

static int dtu_atcmd_reboot(const tokens_struct_type *tok_ptr, AtCmdResponse * resp)
{
	OSA_STATUS status;
	dsat_result_enum_type result = DSAT_OK;

	status = OSATimerStart(uart_task_timer_ref, 10, 0, reboot_callback, 0);
	ASSERT(status == OS_SUCCESS);	

	return result;
}

static int dtu_atcmd_entm(const tokens_struct_type *tok_ptr, AtCmdResponse * resp)
{
	dsat_result_enum_type result = DSAT_OK;
	
	serial_mode = DSAT_DATA_MODE;

	return result;
}

static int dtu_atcmd_ennat(const tokens_struct_type *tok_ptr, AtCmdResponse * resp)
{
	dsat_result_enum_type result = DSAT_OK;
	
	send_close_uart_at_cmd();
	
	OSATaskSleep(50);
	
	return result;
}


static int dtu_atcmd_csq(const tokens_struct_type *tok_ptr, AtCmdResponse * resp)
{
	dsat_result_enum_type result = DSAT_ERROR;

	if (tok_ptr->op == (NA) || tok_ptr->op == (NA|QU)) {
		int pSignalRssi = ps_GetCsq();
		int pSignalBer = 99;

		sprintf(resp->response, "+CSQ:%d,,%d", pSignalRssi, pSignalBer);
		result = DSAT_OK;
	}
	return result;
}
static int dtu_atcmd_sysinfo(const tokens_struct_type *tok_ptr, AtCmdResponse * resp)
{
	dsat_result_enum_type result = DSAT_ERROR;
	
	if (tok_ptr->op == (NA) || tok_ptr->op == (NA|QU)) {
		if(ps_GetNetworkReady()){
			sprintf(resp->response, "+SYSINFO:%d,%s", 2, "LTE");
		} else {
			sprintf(resp->response, "+SYSINFO:%d,%s", 0, "No Network");
		}

		result = DSAT_OK;
	}
	
	return result;
}

static int dtu_atcmd_ver(const tokens_struct_type *tok_ptr, AtCmdResponse * resp)
{
	dsat_result_enum_type result = DSAT_OK;
	sprintf(resp->response, "+VER:%s", "DTU_V1.1");
	return result;
}

static int dtu_atcmd_iccid(const tokens_struct_type *tok_ptr, AtCmdResponse * resp)
{
	dsat_result_enum_type result = DSAT_ERROR;

	if (tok_ptr->op == (NA) || tok_ptr->op == (NA|QU)) {
		char iccid[32] = {0};

		dev_GetSIMIccid((char *)iccid, sizeof(iccid));

		sprintf(resp->response, "+ICCID:%s", iccid);

		result = DSAT_OK;
	}
	
	return result;
}

static int dtu_atcmd_imei(const tokens_struct_type *tok_ptr, AtCmdResponse * resp)
{
	dsat_result_enum_type result = DSAT_ERROR;

	if (tok_ptr->op == (NA) || tok_ptr->op == (NA|QU)) {
		char imei[32] = {0};

		dev_GetSIMImsi((char *)imei, sizeof(imei));

		sprintf(resp->response, "+IMEI:%s", imei);

		result = DSAT_OK;
	}
	
	return result;
}

static int dtu_atcmd_clear(const tokens_struct_type *tok_ptr, AtCmdResponse * resp)
{
	dsat_result_enum_type result = DSAT_OK;

	return result;
}

static int dtu_atcmd_sock(const tokens_struct_type *tok_ptr, AtCmdResponse * resp)
{
    dsat_result_enum_type result = DSAT_ERROR;

    return result;
}

static int dtu_atcmd_socken(const tokens_struct_type *tok_ptr, AtCmdResponse * resp)
{
    dsat_result_enum_type result = DSAT_ERROR;

    return result;
}

static int dtu_atcmd_socklk(const tokens_struct_type *tok_ptr, AtCmdResponse * resp)
{
    dsat_result_enum_type result = DSAT_ERROR;

    return result;
}
static int dtu_atcmd_regen(const tokens_struct_type *tok_ptr, AtCmdResponse * resp)
{
    dsat_result_enum_type result = DSAT_ERROR;

    return result;
}
static int dtu_atcmd_regtp(const tokens_struct_type *tok_ptr, AtCmdResponse * resp)
{
    dsat_result_enum_type result = DSAT_ERROR;

    return result;
}

static int dtu_atcmd_regdt(const tokens_struct_type *tok_ptr, AtCmdResponse * resp)
{
    dsat_result_enum_type result = DSAT_ERROR;

    return result;
}

static int dtu_atcmd_regsnd(const tokens_struct_type *tok_ptr, AtCmdResponse * resp)
{
    dsat_result_enum_type result = DSAT_ERROR;

    return result;
}

static int dtu_atcmd_regfmt(const tokens_struct_type *tok_ptr, AtCmdResponse * resp)
{
    dsat_result_enum_type result = DSAT_ERROR;

    return result;
}
static int dtu_atcmd_hearten(const tokens_struct_type *tok_ptr, AtCmdResponse * resp)
{
    dsat_result_enum_type result = DSAT_ERROR;

    return result;
}
static int dtu_atcmd_hearsnd(const tokens_struct_type *tok_ptr, AtCmdResponse * resp)
{
    dsat_result_enum_type result = DSAT_OK;

    return result;
}

static int dtu_atcmd_hearttm(const tokens_struct_type *tok_ptr, AtCmdResponse * resp)
{
    dsat_result_enum_type result = DSAT_ERROR;

    return result;
}
static int dtu_atcmd_heartdt(const tokens_struct_type *tok_ptr, AtCmdResponse * resp)
{
    dsat_result_enum_type result = DSAT_ERROR;

    return result;
}

static int dtu_atcmd_heartfmt(const tokens_struct_type *tok_ptr, AtCmdResponse * resp)
{
    dsat_result_enum_type result = DSAT_ERROR;

    return result;
}

static int dtu_atcmd_debug(const tokens_struct_type *tok_ptr, AtCmdResponse * resp)
{
	dsat_result_enum_type result = DSAT_ERROR;

	if (tok_ptr->op == (NA|EQ|AR)) {
		if (0 == strcasecmp((char *)tok_ptr->arg[0],"1")) {
			dtu_debug = 1;
			result = DSAT_OK;
		} else if (0 == strcasecmp((char *)tok_ptr->arg[0],"0")) {
			dtu_debug = 0;
			result = DSAT_OK;
		} else {
			result = DSAT_ERROR;
		}
	}

	return result;
}

static int dtu_atcmd_socketcfg(const tokens_struct_type *tok_ptr, AtCmdResponse * resp)
{
	int i;
	int link_id = 0;
	dsat_result_enum_type result = DSAT_ERROR;
	socket_info_t *sockcfg_ptr = NULL;
		
	if (tok_ptr->op == (NA) || tok_ptr->op == (NA|QU)) {
		result = DSAT_OK;
	} else if (tok_ptr->op == (NA|EQ|AR)) {
		
		if (tok_ptr->args_found < 2){
			result = DSAT_ERROR;
			return result;
		}

		for (i = 0; i < tok_ptr->args_found; i++){
			dsat_strip_quotes_out(tok_ptr->arg[i], tok_ptr->arg[i], MAX_LINE_SIZE);	
			LOG_PRINTF("tok_ptr->arg[%d]: %s", i, (char *)tok_ptr->arg[i]);
		}

		if (0 == strcasecmp((char *)tok_ptr->arg[1],"0")) {
			link_id = 0;
		} else if (0 == strcasecmp((char *)tok_ptr->arg[1],"1")) {
			link_id = 1;
		} else {
			result = DSAT_ERROR;
			return result;
		}

		sockcfg_ptr = dtu_socket_cfg_get(link_id);
	
		if(strncasecmp ((char *)tok_ptr->arg[0], "socket/mode", strlen("socket/mode")) == 0)
		{
			if (tok_ptr->args_found == 2){
				sprintf(resp->response, "+SOCKETCFG:\"socket/mode\",%d,%d", link_id, sockcfg_ptr->mode);
				result = DSAT_OK;
				return result;
			}
		
			if (0 == strcasecmp((char *)tok_ptr->arg[2],"1")) {
				sockcfg_ptr->mode = 1;
				result = DSAT_OK;
			} else if (0 == strcasecmp((char *)tok_ptr->arg[2],"0")) {
				sockcfg_ptr->mode = 0;
				result = DSAT_OK;
			} else {
				result = DSAT_ERROR;
			}
		}			
		else	if(strncasecmp ((char *)tok_ptr->arg[0], "socket/tls", strlen("socket/tls")) == 0)
		{
			if (tok_ptr->args_found == 2){
				sprintf(resp->response, "+SOCKETCFG:\"socket/tls\",%d,%d", link_id, sockcfg_ptr->tls);
				result = DSAT_OK;
				return result;
			}
		
			if (0 == strcasecmp((char *)tok_ptr->arg[2],"1")) {
				sockcfg_ptr->tls = 1;
				result = DSAT_OK;
			} else if (0 == strcasecmp((char *)tok_ptr->arg[2],"0")) {
				sockcfg_ptr->tls = 0;
				result = DSAT_OK;
			} else {
				result = DSAT_ERROR;
			}
		}			
		else	if(strncasecmp((char *)tok_ptr->arg[0], "socket/timeout", strlen("socket/timeout")) == 0)
		{		
			if (tok_ptr->args_found == 2){
				sprintf(resp->response, "+SOCKETCFG:\"socket/timeout\",%d,%d", link_id, sockcfg_ptr->timeout/1000);
				result = DSAT_OK;
				return result;
			}		

			if (atoi((char *)tok_ptr->arg[2]) > 0 && atoi((char *)tok_ptr->arg[2]) <= 256){
				sockcfg_ptr->timeout = atoi((char *)tok_ptr->arg[1])*1000;
				result = DSAT_OK;			
			} else {
				result = DSAT_ERROR;				
			}			
		}
		else	if(strncasecmp((char *)tok_ptr->arg[0], "socket/keepalive", strlen("socket/keepalive")) == 0)
		{		
			if (tok_ptr->args_found == 2){
				sprintf(resp->response, "+SOCKETCFG:\"socket/keepalive\",%d,%d,%d,%d,%d", 
																	link_id,
																	sockcfg_ptr->keepalive,
																	sockcfg_ptr->keepidle,
																	sockcfg_ptr->keepinterval,
																	sockcfg_ptr->keepcount);
				result = DSAT_OK;
				return result;
			}

			if (!(atoi((char *)tok_ptr->arg[2]) >= 0 && atoi((char *)tok_ptr->arg[2]) <= 1)){				
				result = DSAT_ERROR;
				return result;		
			}

			if (!(atoi((char *)tok_ptr->arg[3]) >= 30 && atoi((char *)tok_ptr->arg[3]) <= 36000)){				
				result = DSAT_ERROR;
				return result;		
			}
				
			if (!(atoi((char *)tok_ptr->arg[4]) >= 5 && atoi((char *)tok_ptr->arg[4]) <= 360)){				
				result = DSAT_ERROR;
				return result;		
			}	

			if (!(atoi((char *)tok_ptr->arg[5]) >= 1 && atoi((char *)tok_ptr->arg[5]) <= 10)){				
				result = DSAT_ERROR;
				return result;		
			}
			sockcfg_ptr->keepalive = atoi((char *)tok_ptr->arg[2]);
			sockcfg_ptr->keepidle = atoi((char *)tok_ptr->arg[3]);		
			sockcfg_ptr->keepinterval = atoi((char *)tok_ptr->arg[4]);		
			sockcfg_ptr->keepcount = atoi((char *)tok_ptr->arg[5]);	

			result = DSAT_OK;
		}	
		else	if(strncasecmp((char *)tok_ptr->arg[0], "socket/login", strlen("socket/login")) == 0)
		{		
			if (tok_ptr->args_found == 1){
				sprintf(resp->response, "+SOCKETCFG:\"socket/login\",%d,%d,\"%s\"\"%s\"", 
														link_id,
														sockcfg_ptr->login_timeout,
														sockcfg_ptr->login_buf,
														sockcfg_ptr->login_resp_buf);
				result = DSAT_OK;
				return result;
			}


			if (!(atoi((char *)tok_ptr->arg[2]) >= 0  && atoi((char *)tok_ptr->arg[2]) <= 120)){
				result = DSAT_ERROR;
				return result;				
			}	

			if (!(strlen((char *)tok_ptr->arg[3]) >= 0 && strlen((char *)tok_ptr->arg[3]) <= MAX_LINE_SIZE/2)){
				result = DSAT_ERROR;
				return result;				
			}	

			if (!(strlen((char *)tok_ptr->arg[4]) >= 0 && strlen((char *)tok_ptr->arg[4]) <= MAX_LINE_SIZE/2)){
				result = DSAT_ERROR;
				return result;				
			}	

			sockcfg_ptr->login_timeout = atoi((char *)tok_ptr->arg[2]);

			memset(sockcfg_ptr->login_buf, 0x00, sizeof(sockcfg_ptr->login_buf));
			memcpy(sockcfg_ptr->login_buf, (char *)tok_ptr->arg[3], strlen((char *)tok_ptr->arg[3]));
			sockcfg_ptr->login_buf_len = strlen((char *)tok_ptr->arg[3]);

			memset(sockcfg_ptr->login_resp_buf, 0x00, sizeof(sockcfg_ptr->login_resp_buf));
			memcpy(sockcfg_ptr->login_resp_buf, (char *)tok_ptr->arg[4], strlen((char *)tok_ptr->arg[4]));
			sockcfg_ptr->login_resp_buf_len = strlen((char *)tok_ptr->arg[4]);

			result = DSAT_OK;
		}					
		else	if(strncasecmp((char *)tok_ptr->arg[0], "socket/heart", strlen("socket/heart")) == 0)
		{		
			if (tok_ptr->args_found == 1){
				sprintf(resp->response, "+SOCKETCFG:\"socket/heart\",%d,%d,\"%s\",\"%s\"", 
														link_id,
														sockcfg_ptr->heart_interval,
														sockcfg_ptr->heart_buf,
														sockcfg_ptr->heart_resp_buf);
				result = DSAT_OK;
				return result;
			}

			if (!(atoi((char *)tok_ptr->arg[2]) >= 0 && atoi((char *)tok_ptr->arg[2]) <= 1200)){
				result = DSAT_ERROR;
				return result;				
			}	

			if (!(strlen((char *)tok_ptr->arg[3]) >= 0 && strlen((char *)tok_ptr->arg[3]) <= MAX_LINE_SIZE/2)){
				result = DSAT_ERROR;
				return result;				
			}	

			if (!(strlen((char *)tok_ptr->arg[4]) >= 0 && strlen((char *)tok_ptr->arg[4]) <= MAX_LINE_SIZE/2)){
				result = DSAT_ERROR;
				return result;				
			}	

			sockcfg_ptr->heart_interval = atoi((char *)tok_ptr->arg[2]);	

			memset(sockcfg_ptr->heart_buf, 0x00, sizeof(sockcfg_ptr->heart_buf));
			memcpy(sockcfg_ptr->heart_buf, (char *)tok_ptr->arg[3], strlen((char *)tok_ptr->arg[3]));
			sockcfg_ptr->heart_buf_len = strlen((char *)tok_ptr->arg[3]);

			memset(sockcfg_ptr->heart_resp_buf, 0x00, sizeof(sockcfg_ptr->heart_resp_buf));
			memcpy(sockcfg_ptr->heart_resp_buf, (char *)tok_ptr->arg[4], strlen((char *)tok_ptr->arg[4]));
			sockcfg_ptr->heart_resp_buf_len = strlen((char *)tok_ptr->arg[4]);

			result = DSAT_OK;
		}			
		else	if(strncasecmp((char *)tok_ptr->arg[0], "socket/host", strlen("socket/host")) == 0)
		{		
			if (tok_ptr->args_found == 2){
				sprintf(resp->response, "+SOCKETCFG:\"socket/host\",%d,\"%s\",%d", link_id, sockcfg_ptr->host, sockcfg_ptr->port);
				result = DSAT_OK;
				return result;
			}

			if (tok_ptr->args_found == 4
				&& (strlen((char *)tok_ptr->arg[2]) > 0 && strlen((char *)tok_ptr->arg[2]) <= MAX_LINE_SIZE/2)
				&& (atoi((char *)tok_ptr->arg[3]) >= 0 && atoi((char *)tok_ptr->arg[3]) <= 65535 ))
			{
				memset(sockcfg_ptr->host, 0x00, sizeof(sockcfg_ptr->host));
				memcpy(sockcfg_ptr->host, (char *)tok_ptr->arg[2], strlen((char *)tok_ptr->arg[2]));

				sockcfg_ptr->port = atoi((char *)tok_ptr->arg[3]);
				
				result = DSAT_OK;				
			} else {
				result = DSAT_ERROR;
			}				
		}		
		
		if (result == DSAT_OK){
			dtu_socket_cfg_update_nvm(link_id);
		}
	}
	return result;
}

extern int dtu_socket_status(int link_id);

static int dtu_atcmd_socketsta(const tokens_struct_type *tok_ptr, AtCmdResponse * resp)
{
	dsat_result_enum_type result = DSAT_OK;
	
	int link_id = 0;

	if (tok_ptr->op == (NA|EQ|AR)) {
		
		if (tok_ptr->args_found != 1){
			result = DSAT_ERROR;
			return result;
		}

		if (0 == strcasecmp((char *)tok_ptr->arg[0],"0")) {
			link_id = 0;
			sprintf(resp->response, "+SOCKETSTA:%d,%d", link_id, dtu_socket_status(link_id));
			result = DSAT_OK;
		} else if (0 == strcasecmp((char *)tok_ptr->arg[0],"1")) {
			link_id = 1;
			sprintf(resp->response, "+SOCKETSTA:%d,%d", link_id, dtu_socket_status(link_id));
			result = DSAT_OK;
		} else {
			result = DSAT_ERROR;
		}
	}

	return result;
}

static int dtu_atcmd_socketclr(const tokens_struct_type *tok_ptr, AtCmdResponse * resp)
{
	dsat_result_enum_type result = DSAT_OK;
	
	int link_id = 0;

	if (tok_ptr->op == (NA|EQ|AR)) {
		
		if (tok_ptr->args_found != 1){
			result = DSAT_ERROR;
			return result;
		}

		if (0 == strcasecmp((char *)tok_ptr->arg[0],"0")) {
			link_id = 0;
			result = DSAT_OK;
		} else if (0 == strcasecmp((char *)tok_ptr->arg[0],"1")) {
			link_id = 1;
			result = DSAT_OK;
		} else {
			result = DSAT_ERROR;
			return result;
		}

		dtu_socket_cfg_remove(link_id);

		send_reset_cmd();
	}

	return result;
}


static int dtu_atcmd_socketlist(const tokens_struct_type *tok_ptr, AtCmdResponse * resp)
{
	char output[1024] = {0};
	int link_id = 0;
	dsat_result_enum_type result = DSAT_OK;
	socket_info_t *sockcfg_ptr = NULL;

	if (tok_ptr->op == (NA|EQ|AR)) {
		
		if (tok_ptr->args_found != 1){
			result = DSAT_ERROR;
			return result;
		}

		if (0 == strcasecmp((char *)tok_ptr->arg[0],"0")) {
			link_id = 0;
			result = DSAT_OK;
		} else if (0 == strcasecmp((char *)tok_ptr->arg[0],"1")) {
			link_id = 1;
			result = DSAT_OK;
		} else {
			result = DSAT_ERROR;
			return result;
		}

		sockcfg_ptr = dtu_socket_cfg_get(link_id);

		send_string_to_uart("\r\n");
		//sprintf(output, "+SOCKETLIST: \"socket/mode\",%d,%d\r\n", link_id, sockcfg_ptr->mode);
		//send_string_to_uart(output);
		//sprintf(output, "+SOCKETLIST: \"socket/tls\",%d,%d\r\n", link_id, sockcfg_ptr->tls);
		//send_string_to_uart(output);
		sprintf(output, "+SOCKETLIST: \"socket/host\",%d,\"%s\",%d\r\n", link_id, sockcfg_ptr->host, sockcfg_ptr->port);
		send_string_to_uart(output);
		sprintf(output, "+SOCKETLIST: \"socket/login\",%d,%d,\"%s\",\"%s\"\r\n", link_id, sockcfg_ptr->login_timeout, sockcfg_ptr->login_buf, sockcfg_ptr->login_resp_buf);
		send_string_to_uart(output);		
		sprintf(output, "+SOCKETLIST: \"socket/heart\",%d,%d,\"%s\",\"%s\"\r\n", link_id, sockcfg_ptr->heart_interval, sockcfg_ptr->heart_buf, sockcfg_ptr->heart_resp_buf);
		send_string_to_uart(output);
		sprintf(output, "+SOCKETLIST: \"socket/keepalive\",%d,%d,%d,%d,%d\r\n", link_id, sockcfg_ptr->keepalive, sockcfg_ptr->keepidle, sockcfg_ptr->keepinterval, sockcfg_ptr->keepcount);		
		send_string_to_uart(output);		
	}
	
	return result;
}

static int dtu_atcmd_mqttcfg(const tokens_struct_type *tok_ptr, AtCmdResponse * resp)
{
	int i;
	dsat_result_enum_type result = DSAT_ERROR;
	mqtt_info_t *mqtt_ptr = mqttcfg_get();
		
	if (tok_ptr->op == (NA) || tok_ptr->op == (NA|QU)) {
		result = DSAT_OK;
	} else if (tok_ptr->op == (NA|EQ|AR)) {
		
		for (i = 0; i < tok_ptr->args_found; i++){
			dsat_strip_quotes_out(tok_ptr->arg[i], tok_ptr->arg[i], MAX_LINE_SIZE);	
			LOG_PRINTF("tok_ptr->arg[%d]: %s", i, (char *)tok_ptr->arg[i]);
		}
	
		if(strncasecmp ((char *)tok_ptr->arg[0], "mqtt/mode", strlen("mqtt/mode")) == 0)
		{
			if (tok_ptr->args_found == 1){
				sprintf(resp->response, "+MQTTCFG:\"mqtt/mode\",%d", mqtt_ptr->mode);
				result = DSAT_OK;
				return result;
			}
		
			if (0 == strcasecmp((char *)tok_ptr->arg[1],"1")) {
				mqtt_ptr->mode = 1;
				result = DSAT_OK;
			} else if (0 == strcasecmp((char *)tok_ptr->arg[1],"0")) {
				mqtt_ptr->mode = 0;
				result = DSAT_OK;
			} else {
				result = DSAT_ERROR;
			}
		}	
		else	if(strncasecmp ((char *)tok_ptr->arg[0], "mqtt/log", strlen("mqtt/log")) == 0)
		{
			if (tok_ptr->args_found == 1){
				sprintf(resp->response, "+MQTTCFG:\"mqtt/log\",%d", mqtt_ptr->debug);
				result = DSAT_OK;
				return result;
			}
		
			if (0 == strcasecmp((char *)tok_ptr->arg[1],"1")) {
				mqtt_ptr->debug = 1;
				result = DSAT_OK;
			} else if (0 == strcasecmp((char *)tok_ptr->arg[1],"0")) {
				mqtt_ptr->debug = 0;
				result = DSAT_OK;
			} else {
				result = DSAT_ERROR;
			}
		}			
		else	if(strncasecmp ((char *)tok_ptr->arg[0], "mqtt/tls", strlen("mqtt/tls")) == 0)
		{
			if (tok_ptr->args_found == 1){
				sprintf(resp->response, "+MQTTCFG:\"mqtt/tls\",%d", mqtt_ptr->tls);
				result = DSAT_OK;
				return result;
			}
		
			if (0 == strcasecmp((char *)tok_ptr->arg[1],"1")) {
				mqtt_ptr->tls = 1;
				result = DSAT_OK;
			} else if (0 == strcasecmp((char *)tok_ptr->arg[1],"0")) {
				mqtt_ptr->tls = 0;
				result = DSAT_OK;
			} else {
				result = DSAT_ERROR;
			}
		}			
		else	if(strncasecmp ((char *)tok_ptr->arg[0], "mqtt/version", strlen("mqtt/version")) == 0)
		{		
		
		}		
		else	if(strncasecmp((char *)tok_ptr->arg[0], "mqtt/timeout", strlen("mqtt/timeout")) == 0)
		{		
			if (tok_ptr->args_found == 1){
				sprintf(resp->response, "+MQTTCFG:\"mqtt/timeout\",%d", mqtt_ptr->timeout/1000);
				result = DSAT_OK;
				return result;
			}		

			if (atoi((char *)tok_ptr->arg[1]) > 0 && atoi((char *)tok_ptr->arg[1]) <= 256){
				mqtt_ptr->timeout = atoi((char *)tok_ptr->arg[1])*1000;
				result = DSAT_OK;			
			} else {
				//mqtt_ptr->timeout = CORE_MQTT_DEFAULT_CONNECT_TIMEOUT_MS;
				result = DSAT_ERROR;				
			}
			
		}
		else	if(strncasecmp((char *)tok_ptr->arg[0], "mqtt/clean", strlen("mqtt/clean")) == 0)
		{		
			if (tok_ptr->args_found == 1){
				sprintf(resp->response, "+MQTTCFG:\"mqtt/clean\",%d", mqtt_ptr->clean);
				result = DSAT_OK;
				return result;
			}
		
			if (0 == strcasecmp((char *)tok_ptr->arg[1],"1")) {
				mqtt_ptr->clean = 1;
			} else {
				mqtt_ptr->clean = 0;
			}

			result = DSAT_OK;		
		}
		else	if(strncasecmp((char *)tok_ptr->arg[0], "mqtt/keepalivexx", strlen("mqtt/keepalivexx")) == 0)
		{		
			if (tok_ptr->args_found == 1){
				sprintf(resp->response, "+MQTTCFG:\"mqtt/keepalive\",%d", mqtt_ptr->keepalive);
				result = DSAT_OK;
				return result;
			}

			if (atoi((char *)tok_ptr->arg[1]) > 0 && atoi((char *)tok_ptr->arg[1]) <= 3600){
				mqtt_ptr->keepalive = atoi((char *)tok_ptr->arg[1]);
				result = DSAT_OK;		
			} else {
				//mqtt_ptr->keepalive = CORE_MQTT_DEFAULT_KEEPALIVE_S;
				result = DSAT_ERROR;				
			}			
		}	
		else	if(strncasecmp((char *)tok_ptr->arg[0], "mqtt/heartbeat", strlen("mqtt/heartbeat")) == 0)
		{		
			if (tok_ptr->args_found == 1){
				sprintf(resp->response, "+MQTTCFG:\"mqtt/heartbeat\",%d", mqtt_ptr->heartbeat);
				result = DSAT_OK;
				return result;
			}

			if (atoi((char *)tok_ptr->arg[1]) > 10 && atoi((char *)tok_ptr->arg[1]) <= 1200){
				mqtt_ptr->heartbeat = atoi((char *)tok_ptr->arg[1])*1000;
				result = DSAT_OK;		
			} else {
				//mqtt_ptr->heartbeat = CORE_MQTT_DEFAULT_HEARTBEAT_INTERVAL_MS;
				result = DSAT_ERROR;				
			}			
		}			
		else	if(strncasecmp((char *)tok_ptr->arg[0], "mqtt/host", strlen("mqtt/host")) == 0)
		{		
			if (tok_ptr->args_found == 1){
				sprintf(resp->response, "+MQTTCFG:\"mqtt/host\",\"%s\",%d", mqtt_ptr->host, mqtt_ptr->port);
				result = DSAT_OK;
				return result;
			}

			if (tok_ptr->args_found == 3
				&& (strlen((char *)tok_ptr->arg[1]) >= 0 && strlen((char *)tok_ptr->arg[1]) < MQTT_CFG_MAX_LEN)
				&& (atoi((char *)tok_ptr->arg[2]) >= 0 && atoi((char *)tok_ptr->arg[2]) <= 65535 ))
			{
				memset(mqtt_ptr->host, 0x00, sizeof(mqtt_ptr->host));
				memcpy(mqtt_ptr->host, (char *)tok_ptr->arg[1], strlen((char *)tok_ptr->arg[1]));

				mqtt_ptr->port = atoi((char *)tok_ptr->arg[2]);
				
				result = DSAT_OK;				
			} else {
				result = DSAT_ERROR;				
			}				
		}
		else	if(strncasecmp((char *)tok_ptr->arg[0], "mqtt/clientid", strlen("mqtt/clientid")) == 0)
		{		
			if (tok_ptr->args_found == 1){
				sprintf(resp->response, "+MQTTCFG:\"mqtt/clientid\",\"%s\"", mqtt_ptr->clientid);
				result = DSAT_OK;
				return result;
			}

			if (strlen((char *)tok_ptr->arg[1]) >= 0 && strlen((char *)tok_ptr->arg[1]) < MQTT_CFG_MAX_LEN){
				memset(mqtt_ptr->clientid, 0x00, sizeof(mqtt_ptr->clientid));
				memcpy(mqtt_ptr->clientid, (char *)tok_ptr->arg[1], strlen((char *)tok_ptr->arg[1]));
				result = DSAT_OK;				
			} else {
				result = DSAT_ERROR;				
			}	
		}		
		else	if(strncasecmp((char *)tok_ptr->arg[0], "mqtt/username", strlen("mqtt/username")) == 0)
		{		
			if (tok_ptr->args_found == 1){
				sprintf(resp->response, "+MQTTCFG:\"mqtt/username\",\"%s\"", mqtt_ptr->username);
				result = DSAT_OK;
				return result;
			}

			if (strlen((char *)tok_ptr->arg[1]) >= 0 && strlen((char *)tok_ptr->arg[1]) < MQTT_CFG_MAX_LEN){
				memset(mqtt_ptr->username, 0x00, sizeof(mqtt_ptr->username));
				memcpy(mqtt_ptr->username, (char *)tok_ptr->arg[1], strlen((char *)tok_ptr->arg[1]));
				result = DSAT_OK;				
			} else {
				result = DSAT_ERROR;	
			}			
		}		
		else	if(strncasecmp((char *)tok_ptr->arg[0], "mqtt/password", strlen("mqtt/password")) == 0)
		{		
			if (tok_ptr->args_found == 1){
				sprintf(resp->response, "+MQTTCFG:\"mqtt/password\",\"%s\"", mqtt_ptr->password);
				result = DSAT_OK;
				return result;
			}

			if (strlen((char *)tok_ptr->arg[1]) >= 0 && strlen((char *)tok_ptr->arg[1]) < MQTT_CFG_MAX_LEN){
				memset(mqtt_ptr->password, 0x00, sizeof(mqtt_ptr->password));
				memcpy(mqtt_ptr->password, (char *)tok_ptr->arg[1], strlen((char *)tok_ptr->arg[1]));
				result = DSAT_OK;				
			} else {
				result = DSAT_ERROR;	
			}			
		}
		else	if(strncasecmp((char *)tok_ptr->arg[0], "mqtt/subtopic", strlen("mqtt/subtopic")) == 0)
		{	
			if (tok_ptr->args_found == 1){
				sprintf(resp->response, "+MQTTCFG:\"mqtt/subtopic\",\"%s\",%d", mqtt_ptr->sub_topic, mqtt_ptr->sub_qos);
				result = DSAT_OK;
				return result;
			}

			if (tok_ptr->args_found == 3
				&& (strlen((char *)tok_ptr->arg[1]) >= 0 && strlen((char *)tok_ptr->arg[1]) < MQTT_CFG_MAX_LEN)
				&& (atoi((char *)tok_ptr->arg[2]) >= 0 && atoi((char *)tok_ptr->arg[2]) <= 2 ))
			{
				memset(mqtt_ptr->sub_topic, 0x00, sizeof(mqtt_ptr->sub_topic));
				memcpy(mqtt_ptr->sub_topic, (char *)tok_ptr->arg[1], strlen((char *)tok_ptr->arg[1]));

				mqtt_ptr->sub_qos = atoi((char *)tok_ptr->arg[2]);
				
				result = DSAT_OK;				
			} else {
				result = DSAT_ERROR;	
			}			
		}	
		else	if(strncasecmp((char *)tok_ptr->arg[0], "mqtt/pubtopic", strlen("mqtt/pubtopic")) == 0)
		{		
			if (tok_ptr->args_found == 1){
				sprintf(resp->response, "+MQTTCFG:\"mqtt/pubtopic\",\"%s\",%d", mqtt_ptr->pub_topic, mqtt_ptr->pub_qos);
				result = DSAT_OK;
				return result;
			}

			if (tok_ptr->args_found == 3
				&& (strlen((char *)tok_ptr->arg[1]) >= 0 && strlen((char *)tok_ptr->arg[1]) < MQTT_CFG_MAX_LEN)
				&& (atoi((char *)tok_ptr->arg[2]) >= 0 && atoi((char *)tok_ptr->arg[2]) <= 2 ))
			{
				memset(mqtt_ptr->pub_topic, 0x00, sizeof(mqtt_ptr->pub_topic));
				memcpy(mqtt_ptr->pub_topic, (char *)tok_ptr->arg[1], strlen((char *)tok_ptr->arg[1]));

				mqtt_ptr->pub_qos = atoi((char *)tok_ptr->arg[2]);

				//mqtt_ptr->pub_retained = atoi((char *)tok_ptr->arg[3]);
				
				result = DSAT_OK;				
			} else {
				result = DSAT_ERROR;	
			}				
		}			
		else	if(strncasecmp((char *)tok_ptr->arg[0], "aliyun/product_key", strlen("aliyun/product_key")) == 0)
		{
			if (tok_ptr->args_found == 1){
				sprintf(resp->response, "+MQTTCFG:\"aliyun/product_key\",\"%s\"", mqtt_ptr->productkey);
				result = DSAT_OK;
				return result;
			}

			if (strlen((char *)tok_ptr->arg[1]) >= 0 && strlen((char *)tok_ptr->arg[1]) < MQTT_CFG_MAX_LEN){
				memset(mqtt_ptr->productkey, 0x00, sizeof(mqtt_ptr->productkey));
				memcpy(mqtt_ptr->productkey, (char *)tok_ptr->arg[1], strlen((char *)tok_ptr->arg[1]));
				result = DSAT_OK;				
			} else {
				result = DSAT_ERROR;	
			}			
		}
		else	if(strncasecmp((char *)tok_ptr->arg[0], "aliyun/device_name", strlen("aliyun/device_name")) == 0)
		{
			if (tok_ptr->args_found == 1){
				sprintf(resp->response, "+MQTTCFG:\"aliyun/device_name\",\"%s\"", mqtt_ptr->devicename);
				result = DSAT_OK;
				return result;
			}

			if (strlen((char *)tok_ptr->arg[1]) >= 0 && strlen((char *)tok_ptr->arg[1]) < MQTT_CFG_MAX_LEN){
				memset(mqtt_ptr->devicename, 0x00, sizeof(mqtt_ptr->devicename));
				memcpy(mqtt_ptr->devicename, (char *)tok_ptr->arg[1], strlen((char *)tok_ptr->arg[1]));
				result = DSAT_OK;				
			} else {
				result = DSAT_ERROR;	
			}				
		}
		else	if(strncasecmp((char *)tok_ptr->arg[0], "aliyun/device_secret", strlen("aliyun/device_secret")) == 0)
		{
			if (tok_ptr->args_found == 1){
				sprintf(resp->response, "+MQTTCFG:\"aliyun/device_secret\",\"%s\"", mqtt_ptr->devicesecret);
				result = DSAT_OK;
				return result;
			}

			if (strlen((char *)tok_ptr->arg[1]) >= 0 && strlen((char *)tok_ptr->arg[1]) < MQTT_CFG_MAX_LEN){
				memset(mqtt_ptr->devicesecret, 0x00, sizeof(mqtt_ptr->devicesecret));
				memcpy(mqtt_ptr->devicesecret, (char *)tok_ptr->arg[1], strlen((char *)tok_ptr->arg[1]));
				result = DSAT_OK;				
			} else {
				result = DSAT_ERROR;	
			}				
		}
		else	if(strncasecmp((char *)tok_ptr->arg[0], "aliyun/product_secret", strlen("aliyun/product_secret")) == 0)
		{
			if (tok_ptr->args_found == 1){
				sprintf(resp->response, "+MQTTCFG:\"aliyun/product_secret\",\"%s\"", mqtt_ptr->productsecret);
				result = DSAT_OK;
				return result;
			}

			if (strlen((char *)tok_ptr->arg[1]) >= 0 && strlen((char *)tok_ptr->arg[1]) < MQTT_CFG_MAX_LEN){
				memset(mqtt_ptr->productsecret, 0x00, sizeof(mqtt_ptr->productsecret));
				memcpy(mqtt_ptr->productsecret, (char *)tok_ptr->arg[1], strlen((char *)tok_ptr->arg[1]));
				result = DSAT_OK;				
			} else {
				result = DSAT_ERROR;	
			}		
		}
		else	if(strncasecmp((char *)tok_ptr->arg[0], "aliyun/register_mode", strlen("aliyun/register_mode")) == 0)
		{
			if (tok_ptr->args_found == 1){
				sprintf(resp->response, "+MQTTCFG:\"aliyun/register_mode\",%d", mqtt_ptr->register_mode);
				result = DSAT_OK;
				return result;
			}
		
			if (0 == strcasecmp((char *)tok_ptr->arg[1],"0")) {
				mqtt_ptr->register_mode = 0;
				result = DSAT_OK;
			} else if (0 == strcasecmp((char *)tok_ptr->arg[1],"1")) {
				mqtt_ptr->register_mode = 1;
				result = DSAT_OK;
			} else if (0 == strcasecmp((char *)tok_ptr->arg[1],"2")) {
				mqtt_ptr->register_mode = 2;
				result = DSAT_OK;				
			} else {
				result = DSAT_ERROR;
			}

		}	
		
		if (result == DSAT_OK){
			mqttcfg_update_nvm();
		}
	}
	return result;
}


static int dtu_atcmd_mqttclr(const tokens_struct_type *tok_ptr, AtCmdResponse * resp)
{
	dsat_result_enum_type result = DSAT_OK;
	
	mqttcfg_remove();

	send_reset_cmd();
	
	return result;
}


static int dtu_atcmd_mqttlist(const tokens_struct_type *tok_ptr, AtCmdResponse * resp)
{
	char output[512] = {0};
	dsat_result_enum_type result = DSAT_OK;
	mqtt_info_t *mqtt_ptr = mqttcfg_get();

	send_string_to_uart("\r\n");
	sprintf(output, "+MQTTLIST: \"mqtt/mode\",%d\r\n", mqtt_ptr->mode);
	send_string_to_uart(output);
	sprintf(output, "+MQTTLIST: \"mqtt/tls\",%d\r\n", mqtt_ptr->tls);
	send_string_to_uart(output);	
	sprintf(output, "+MQTTLIST: \"mqtt/clean\",%d\r\n", mqtt_ptr->clean);
	send_string_to_uart(output);
	sprintf(output, "+MQTTLIST: \"mqtt/heartbeat\",%d\r\n", mqtt_ptr->heartbeat);
	send_string_to_uart(output);
	sprintf(output, "+MQTTLIST: \"mqtt/host\",\"%s\",%d\r\n", mqtt_ptr->host, mqtt_ptr->port);
	send_string_to_uart(output);
	sprintf(output, "+MQTTLIST: \"mqtt/clientid\",\"%s\"\r\n", mqtt_ptr->clientid);
	send_string_to_uart(output);
	sprintf(output, "+MQTTLIST: \"mqtt/username\",\"%s\"\r\n", mqtt_ptr->username);
	send_string_to_uart(output);
	sprintf(output, "+MQTTLIST: \"mqtt/password\",\"%s\"\r\n", mqtt_ptr->password);
	send_string_to_uart(output);
	sprintf(output, "+MQTTLIST: \"mqtt/subtopic\",\"%s\",%d\r\n", mqtt_ptr->sub_topic, mqtt_ptr->sub_qos);
	send_string_to_uart(output);
	sprintf(output, "+MQTTLIST: \"mqtt/pubtopic\",\"%s\",%d\r\n", mqtt_ptr->pub_topic, mqtt_ptr->pub_qos);
	send_string_to_uart(output);
	sprintf(output, "+MQTTLIST: \"aliyun/register_mode\",%d\r\n", mqtt_ptr->register_mode);
	send_string_to_uart(output);
	sprintf(output, "+MQTTLIST: \"aliyun/product_key\",\"%s\"\r\n", mqtt_ptr->productkey);
	send_string_to_uart(output);
	sprintf(output, "+MQTTLIST: \"aliyun/device_name\",\"%s\"\r\n", mqtt_ptr->devicename);
	send_string_to_uart(output);
	sprintf(output, "+MQTTLIST: \"aliyun/device_secret\",\"%s\"\r\n", mqtt_ptr->devicesecret);
	send_string_to_uart(output);
	sprintf(output, "+MQTTLIST: \"aliyun/product_secret\",\"%s\"\r\n", mqtt_ptr->productsecret);
	send_string_to_uart(output);
	
	return result;
}


typedef struct {
    unsigned char at_cmd_name[AT_CMD_NAME_MAX_LEN + 1];
    int (*proc_func)(const tokens_struct_type *, AtCmdResponse *);
} ATCmdTable;

static ATCmdTable dtu_atcmd_table[] = {
    { "E0", dtu_atcmd_echo},
    { "E1", dtu_atcmd_echo},
    { "+REBOOT", dtu_atcmd_reboot},
    { "+ENTM", dtu_atcmd_entm },
    { "+ENNAT", dtu_atcmd_ennat },
	//{ "+EXNAT", dtu_atcmd_exnat },	//define in ext_at_cmds.c
    { "+CSQ", dtu_atcmd_csq },
    { "+SYSINFO", dtu_atcmd_sysinfo },
    { "+VER", dtu_atcmd_ver },
    { "+ICCID", dtu_atcmd_iccid },
    { "+IMEI", dtu_atcmd_imei },
    { "+CLEAR", dtu_atcmd_clear },    
    { "+SOCK",  dtu_atcmd_sock },
    { "+SOCKEN",  dtu_atcmd_socken },
    { "+SOCKLK",  dtu_atcmd_socklk },    
    { "+REGEN", dtu_atcmd_regen },
    { "+REGTP", dtu_atcmd_regtp },
    { "+REGDT", dtu_atcmd_regdt },
    { "+REGSND", dtu_atcmd_regsnd },
    { "+REGFMT", dtu_atcmd_regfmt },
    { "+HEARTEN", dtu_atcmd_hearten },
    { "+HEARSND", dtu_atcmd_hearsnd },
    { "+HEARTTM", dtu_atcmd_hearttm },
    { "+HEARTDT", dtu_atcmd_heartdt },
    { "+HEARTFMT", dtu_atcmd_heartfmt },    
	{ "+DEBUG", dtu_atcmd_debug },    
    { "+SOCKETCFG", dtu_atcmd_socketcfg }, 
	{ "+SOCKETSTA", dtu_atcmd_socketsta }, 
    { "+SOCKETCLR", dtu_atcmd_socketclr},     
    { "+SOCKETLIST", dtu_atcmd_socketlist }, 	
    { "+MQTTCFG", dtu_atcmd_mqttcfg }, 
    { "+MQTTCLR", dtu_atcmd_mqttclr},     
    { "+MQTTLIST", dtu_atcmd_mqttlist }, 
};


/* process transparent data function */
static void send_serial_data(char *transData, int data_len)
{
    LOG_PRINTF("%s[%d]: DSAT_DATA_MODE recvdata: %s\n", __FUNCTION__, __LINE__, transData);
}


/* This function send data to uart. */
void send_to_uart(char *buffer, int length)
{
	LOG_PRINTF("%s[%d] length=%d,buffer=%s\n", __FUNCTION__, __LINE__, length, buffer);

	hal_UartWrite(UART_PORT_2, (UINT8 *)buffer, length);
}

void send_string_to_uart(char *string)
{
	hal_UartWrite(UART_PORT_2, (UINT8 *)string, strlen(string));
}


static void check_serial_mode(uartMsgData_S *msgUartData)
{
    LOG_PRINTF("%s[%d]:check_serial_mode: %d\n", __FUNCTION__, __LINE__, serial_mode);
    if (serial_mode == DSAT_DATA_MODE) {
        if(msgUartData->len == 3 && memcmp(msgUartData->data, "+++", 3) == 0) {
            serial_mode = 0xFF;
            send_to_uart("a", 1);

            // T0D0: Exit 0xff mode after 3 seconds, change to DSAT_DATA_MODE
        }
    } else if (serial_mode == 0xFF) {
        if(msgUartData->len == 1 && memcmp(msgUartData->data, "a", 1) == 0) {
            serial_mode = DSAT_AT_MODE;
            send_to_uart("+ok", 3);
        } else {
            serial_mode = DSAT_DATA_MODE;
            if (msgUartData->len > 0)
                send_serial_data((char *)msgUartData->data, msgUartData->len);
        }
    }
}

static void sendResponse(AtCmdResponse *response)
{
    static const char *MSG_OK="\r\nOK\r\n";
    static const char *MSG_ERROR="\r\nERROR\r\n";
    char resp_buffer[MAX_LINE_SIZE];
    const char *msg = NULL;
    int sz;


    if (response->result == DSAT_OK) {
        if (response->response && response->response[0]) {
            sz = sprintf((char *)resp_buffer, "\r\n%s\r\n", response->response);
            send_to_uart((char *)resp_buffer, sz);
        }
        msg = MSG_OK;
    } else {
        msg = MSG_ERROR;
    }

    send_to_uart((char *)msg, strlen(msg));
}

static int process_at_cmd_line(const tokens_struct_type *tok_ptr)
{
    int i;
    AtCmdResponse *response;

    response = calloc(1, sizeof(AtCmdResponse));
    if ( response != NULL ) {
        response->response = malloc(MAX_LINE_SIZE);
        response->response[0] = 0;
        response->result = DSAT_ERROR;

        if ( (tok_ptr->op & (NA)) == NA ) {
            ATCmdTable *atcmd_ptr = dtu_atcmd_table;
            int nCommands = sizeof(dtu_atcmd_table) / sizeof(dtu_atcmd_table[0]);
            for (i=0; i < nCommands; i++, atcmd_ptr++) {
                if(!strncasecmp((char *)atcmd_ptr->at_cmd_name, (char *)tok_ptr->name, strlen((char *)tok_ptr->name))) {
                    response->result = DSAT_OK;
                    if (atcmd_ptr->proc_func != NULL) {
                        response->result = atcmd_ptr->proc_func(tok_ptr, response);
                    }
                    break;
                }
            }
        } else {
            response->result = DSAT_OK;
        }
        sendResponse(response);

        if (response->response)
            free(response->response);
        free(response);
    }
    return 0;
}

static void dump_echo(byte c)
{
    if (dtu_echo){
        UINT8 out[2] = {0};
        out[0] = c;
		hal_UartWrite(UART_PORT_2, out, 1);
    }
}

static int trim_string(byte **start)
{
    byte *end = *start;

    /* Advance to non-space character */
    while (*(*start) == ' ') {
        (*start)++;
    }

    /* Move to end of string */
    while (*end != '\0') {
        (end)++;
    }
    /* Backup to non-space character */
    while ((end >= *start) && *(--end) == ' ') {}

    /* Terminate string after last non-space character */
    *(++end) = '\0';
    return (end - *start);
} /* trim_string */

/*===========================================================================

FUNCTION PARSE_EXTENDED_COMMAND

DESCRIPTION
  This function parses an incoming character string, which is assumed
  to be an extended format AT+ command, with the AT removed. The
  results of the parse are placed in a specified structure for
  further processing.

===========================================================================*/
static int dsatpar_parse_cmd_line (byte *a_ptr, tokens_struct_type *tokens_res)
{
    byte c, quote_toggle;
    byte *working_ptr;
    int result = 0;

    working_ptr = tokens_res->working_at_line;
    tokens_res->op = 0;
    tokens_res->name = working_ptr;
    tokens_res->args_found = 0;
    quote_toggle = 0;

    while ( (c = *a_ptr) == ' ') {
        ++a_ptr;
    }

    while ( (c = *a_ptr) != '\0' ) {
        if ( working_ptr >= &tokens_res->working_at_line[MAX_LINE_SIZE-1] ) {
            result = 1;
            break;
        }

        *working_ptr = c;

        if ( c != ' ' && c != ';' && tokens_res->op == 0 ) {
            tokens_res->op = NA;        /*  Start of Name */
            tokens_res->name = working_ptr;
        }

        else if ( c == '=' && tokens_res->op == NA ) {
            *working_ptr = '\0';                  /*  Terminate Name  */
            tokens_res->arg[0] = working_ptr + 1; /*  Start of (first) argument */
            tokens_res->args_found = 1;
            tokens_res->op |= EQ;
        }

        else if ( c == '?' && (tokens_res->op & (NA|AR)) == NA ) {
            *working_ptr = '\0';                  /*  Terminate Name  */
            tokens_res->arg[0] = working_ptr+1;   /*  Start of (first) argument */
            tokens_res->args_found = 1;
            tokens_res->op |= QU;
        }

        else if ( c != ' ' && c != ';' && tokens_res->op > NA ) {
            tokens_res->op |=AR;        /*  Argument (or comma) actually found  */
        }

        if ( c == '"' && (tokens_res->op & AR) ) {
            quote_toggle = !quote_toggle;
        }

        if ( ! quote_toggle ) {       /*  Allow ';' and ',' inside '"'  */
            if ( c == ',' && (tokens_res->op & AR) ) {
                *working_ptr = '\0';                /*  Terminate current argument  */
                if(tokens_res->args_found) {
                    (void)trim_string(&tokens_res->arg [tokens_res->args_found-1]);
                }
                if ( tokens_res->args_found < MAX_ARG ) {
                    /*  Start of next arg */
                    tokens_res->arg [tokens_res->args_found] = working_ptr + 1;
                    ++tokens_res->args_found;
                } else {
                    result = 1;
                    break;
                }
            }

            if ( c == ';' ) {
                ++a_ptr;
                break;
            }
        }
        ++working_ptr;
        ++a_ptr;
    }

    *working_ptr = '\0';
    tokens_res->end_of_line = working_ptr;

    /* Ensure leading/trailing spaces are trimmed */
    (void)trim_string(&tokens_res->name);
    if (0 < tokens_res->args_found) {
        (void)trim_string(&tokens_res->arg [tokens_res->args_found-1]);
    }
    return result;
}

static void process_at_cmd_mode(const char *atCmdData, int data_len)
{
    dsat_sio_info_s_type *sio_info_ptr = &g_sio_info;
    char *buf_ptr = NULL;
    char cc, atName[MAX_LINE_SIZE] = {0};

    buf_ptr = (char *)atCmdData;
    LOG_PRINTF("%s[%d]: DSAT_AT_MODE recv ATCMD: %s\n", __FUNCTION__, __LINE__, buf_ptr);
    
    while (data_len > 0) {
        cc = *buf_ptr++;
        data_len--;
        
        switch (sio_info_ptr->at_cmd_prep_state) {
        case DSAT_CMD_PREP_STATE_HUNT:
            if ( UPCASE( cc ) == 'A' ) {
                sio_info_ptr->at_cmd_prep_state = DSAT_CMD_PREP_STATE_FOUND_A;
                dump_echo(cc);
            }
            break;

        case DSAT_CMD_PREP_STATE_FOUND_A:
            if ( UPCASE( cc ) == 'T' ) {
                sio_info_ptr->build_cmd_ptr = sio_info_ptr->cmd_line_buffer;
                sio_info_ptr->at_cmd_prep_state = DSAT_CMD_PREP_STATE_FOUND_AT;
                dump_echo(cc);
            } else if ( UPCASE( cc ) != 'A' ) {
                sio_info_ptr->at_cmd_prep_state = DSAT_CMD_PREP_STATE_HUNT;
            }
            break;

        case DSAT_CMD_PREP_STATE_FOUND_AT:
            dump_echo(cc);
            if (cc != '\r') {
                if (sio_info_ptr->build_cmd_ptr >= (sio_info_ptr->cmd_line_buffer
                                                    + sizeof(sio_info_ptr->cmd_line_buffer) - 2)) {
                    sio_info_ptr->at_cmd_prep_state = DSAT_CMD_PREP_STATE_ERROR;
                    sio_info_ptr->cmd_line_buffer[0] = '\0';
                } else {
                    *sio_info_ptr->build_cmd_ptr++ = cc;
                }
            } else {
                tokens_struct_type g_token;

                /*  EOL found, terminate and parse */
                *sio_info_ptr->build_cmd_ptr = '\0';

                /* Parse command line */
                if (dsatpar_parse_cmd_line((byte *)sio_info_ptr->cmd_line_buffer, &g_token) == 0) {
                    /* Process parsed AT commands.  Initial command result code must be
                       OK in order for command line to be processed. */
                    process_at_cmd_line(&g_token);
                }
                
                sio_info_ptr->at_cmd_prep_state = DSAT_CMD_PREP_STATE_HUNT;
            }
            break;
        case DSAT_CMD_PREP_STATE_ERROR:
            sio_info_ptr->at_cmd_prep_state = DSAT_CMD_PREP_STATE_HUNT;
            break;
        default:
            sio_info_ptr->at_cmd_prep_state = DSAT_CMD_PREP_STATE_HUNT;
            break;
        }
    }
}

void mainUartRecvCallback(UINT8 * recv_data, UINT32 recv_len)
{	
	uartMsgData_S uart_data = {0};
	OSA_STATUS osa_status;

	char *tempbuf = (char *)malloc(recv_len+1);
	memset(tempbuf, 0x0, recv_len+1);
	memcpy(tempbuf, (char *)recv_data, recv_len);

	LOG_PRINTF("%s[%d]: recv_len:%d, recv_data:%s\n", __FUNCTION__, __LINE__, recv_len, (char *)(tempbuf)); 
	uart_data.data = (UINT8 *)tempbuf;
	uart_data.len = recv_len;
	uart_data.port = 0;	// main uart

	osa_status = OSAMsgQSend(uart_msgq_ref, sizeof(uartMsgData_S), (UINT8*)&uart_data, OSA_NO_SUSPEND);
	ASSERT(osa_status == OS_SUCCESS);
}

void uart3RecvCallback(UINT8 * recv_data, UINT32 recv_len)
{	
	uartMsgData_S uart_data = {0};
	OSA_STATUS osa_status;

	char *tempbuf = (char *)malloc(recv_len+1);
	memset(tempbuf, 0x0, recv_len+1);
	memcpy(tempbuf, (char *)recv_data, recv_len);

	LOG_PRINTF("%s[%d]: recv_len:%d, recv_data:%s\n", __FUNCTION__, __LINE__, recv_len, (char *)(tempbuf)); 
	uart_data.data = (UINT8 *)tempbuf;
	uart_data.len = recv_len;
	uart_data.port = 1;	// uart3

	osa_status = OSAMsgQSend(uart_msgq_ref, sizeof(uartMsgData_S), (UINT8*)&uart_data, OSA_NO_SUSPEND);
	ASSERT(osa_status == OS_SUCCESS);
}

void send_reset_cmd(void)
{
	OSA_STATUS osa_status;
	uartMsgData_S uart_data = {0};

	uart_data.data = NULL;
	uart_data.len = 0x7F;
	uart_data.port = 0;	// main uart

	osa_status = OSAMsgQSend(uart_msgq_ref, sizeof(uartMsgData_S), (UINT8*)&uart_data, OSA_NO_SUSPEND);
	ASSERT(osa_status == OS_SUCCESS);
}

void send_close_uart_at_cmd(void)
{
	OSA_STATUS osa_status;
	uartMsgData_S uart_data = {0};

	uart_data.data = NULL;
	uart_data.len = 0x1F;
	uart_data.port = 0;	// main uart	

	osa_status = OSAMsgQSend(uart_msgq_ref, sizeof(uartMsgData_S), (UINT8*)&uart_data, OSA_NO_SUSPEND);
	ASSERT(osa_status == OS_SUCCESS);
}

void send_open_uart_at_cmd(void)
{
	OSA_STATUS osa_status;
	uartMsgData_S uart_data = {0};

	uart_data.data = NULL;
	uart_data.len = 0x2F;
	uart_data.port = 0;	// main uart	

	osa_status = OSAMsgQSend(uart_msgq_ref, sizeof(uartMsgData_S), (UINT8*)&uart_data, OSA_NO_SUSPEND);
	ASSERT(osa_status == OS_SUCCESS);
}


extern void send_uart_data_to_mqtt(char *data, int len);

extern int dtu_socket_send_msg_from_uart(int link_id, char *data, int len);

static void uart_thread(void *ptr)
{
	OSA_STATUS status;

	char *tuochuan_buf_0 = malloc( TC_BUFFER_MAXSIZE );
	int tuochuan_buf_0_len = 0;
	
	char *tuochuan_buf_1 = malloc( TC_BUFFER_MAXSIZE );
	int tuochuan_buf_1_len = 0;
	
	UINT32 mqCount;	

	uartMsgData_S uart_data = {0};

	hal_UartOpenCb(UART_PORT_2, 115200, mainUartRecvCallback);

	hal_UartEnableGpio(UART_PORT_3, 10, 11);
	hal_UartOpenCb(UART_PORT_3, 115200, uart3RecvCallback);

	serial_mode = DSAT_DATA_MODE;

	while (1) {
loop:			
		memset(&uart_data, 0x00, sizeof(uartMsgData_S));        
		status = OSAMsgQRecv(uart_msgq_ref, (UINT8 *)&uart_data, sizeof(uartMsgData_S), OSA_SUSPEND);    //recv data from uart

		if (status == OS_SUCCESS){
			// main uart
			if (uart_data.port == 0) {
				// 重启
				if (uart_data.data == NULL && uart_data.len == 0x7F) {
					OSATaskSleep(100);
					dev_CtrlReset(0);
				}
				// UART关闭，切换到通用AT指令模式
				if (uart_data.data == NULL && uart_data.len == 0x1F) {
					hal_UartClose(UART_PORT_2); 
				}	
				// UART打开，切换到当前DTU AT或者透传模式
				if (uart_data.data == NULL && uart_data.len == 0x2F) {	
					hal_UartOpen(UART_PORT_2, 115200, mainUartRecvCallback); 
				}				

				if (uart_data.data && uart_data.len > 0) {
					LOG_PRINTF("%s: len:%d, data:%s\n", __FUNCTION__, uart_data.len, (char *)(uart_data.data));

					check_serial_mode(&uart_data);

					if (serial_mode == DSAT_DATA_MODE) {
					
						LOG_PRINTF("DSAT_DATA_MODE: len:%d, data: %s", uart_data.len, (char *)uart_data.data);

						if (tuochuan_buf_0_len + uart_data.len > TC_BUFFER_MAXSIZE -1) {	
							
							LOG_PRINTF("send_uart_data_to_mqtt 1: len:%d, data: %s", tuochuan_buf_0_len, (char *)tuochuan_buf_0);
							
							send_uart_data_to_mqtt((char *)tuochuan_buf_0, tuochuan_buf_0_len);

							dtu_socket_send_msg_from_uart(0, (char *)tuochuan_buf_0, tuochuan_buf_0_len);

							tuochuan_buf_0_len = 0;												
							memset(tuochuan_buf_0, 0x00, TC_BUFFER_MAXSIZE);
						}

						memcpy(tuochuan_buf_0+tuochuan_buf_0_len, (char *)uart_data.data, uart_data.len);
						tuochuan_buf_0_len += uart_data.len;

						mqCount = 0;
						OSAMsgQPoll(uart_msgq_ref, &mqCount);
						if (mqCount) {
							LOG_PRINTF("%s,mq_count %d,need read", __FUNCTION__, mqCount);					
							if (uart_data.data)
								free(uart_data.data);
							goto loop;
						} else  {
							LOG_PRINTF("%s,mq_count=0,need wait 5ms", __FUNCTION__, mqCount);
							OSATaskSleep(1);
							mqCount = 0;
							OSAMsgQPoll(uart_msgq_ref, &mqCount);
							if (mqCount) {
								LOG_PRINTF("%s,find mq_count %d,need read again", __FUNCTION__, mqCount);
								if (uart_data.data)
									free(uart_data.data);
								goto loop;
							}
						}

						LOG_PRINTF("send_uart_data_to_mqtt 2: len:%d, data: %s", tuochuan_buf_0_len, (char *)tuochuan_buf_0);					
						
						send_uart_data_to_mqtt((char *)tuochuan_buf_0, tuochuan_buf_0_len);

						dtu_socket_send_msg_from_uart(0, (char *)tuochuan_buf_0, tuochuan_buf_0_len);

						tuochuan_buf_0_len = 0;
						memset(tuochuan_buf_0, 0x00, TC_BUFFER_MAXSIZE);
											
					} else if (serial_mode == DSAT_AT_MODE) {
						LOG_PRINTF("serial_mode is DSAT_AT_MODE!\n");
						process_at_cmd_mode((char *)uart_data.data, uart_data.len);
					}				
				}
				
			} else if (uart_data.port == 1) {
				if (uart_data.data && uart_data.len > 0) {

					LOG_PRINTF("%s: len:%d, data:%s\n", __FUNCTION__, uart_data.len, (char *)(uart_data.data));

					if (tuochuan_buf_1_len + uart_data.len > TC_BUFFER_MAXSIZE -1) {	
						
						LOG_PRINTF("send_uart_data_to_mqtt 1: len:%d, data: %s", tuochuan_buf_1_len, (char *)tuochuan_buf_1);
						
						send_uart_data_to_mqtt((char *)tuochuan_buf_1, tuochuan_buf_1_len);

						dtu_socket_send_msg_from_uart(1, (char *)tuochuan_buf_1, tuochuan_buf_1_len);

						tuochuan_buf_1_len = 0;												
						memset(tuochuan_buf_1, 0x00, TC_BUFFER_MAXSIZE);
					}

					memcpy(tuochuan_buf_1+tuochuan_buf_1_len, (char *)uart_data.data, uart_data.len);
					tuochuan_buf_1_len += uart_data.len;

					mqCount = 0;
					OSAMsgQPoll(uart_msgq_ref, &mqCount);
					if (mqCount) {
						LOG_PRINTF("%s,mq_count %d,need read", __FUNCTION__, mqCount);					
						if (uart_data.data)
							free(uart_data.data);
						goto loop;
					} else  {
						LOG_PRINTF("%s,mq_count=0,need wait 5ms", __FUNCTION__, mqCount);
						OSATaskSleep(1);
						mqCount = 0;
						OSAMsgQPoll(uart_msgq_ref, &mqCount);
						if (mqCount) {
							LOG_PRINTF("%s,find mq_count %d,need read again", __FUNCTION__, mqCount);
							if (uart_data.data)
								free(uart_data.data);
							goto loop;
						}
					}

					LOG_PRINTF("send_uart_data_to_mqtt 2: len:%d, data: %s", tuochuan_buf_1_len, (char *)tuochuan_buf_1);					
					
					send_uart_data_to_mqtt((char *)tuochuan_buf_1, tuochuan_buf_1_len);

					dtu_socket_send_msg_from_uart(1, (char *)tuochuan_buf_1, tuochuan_buf_1_len);
					
					tuochuan_buf_1_len = 0;
					memset(tuochuan_buf_1, 0x00, TC_BUFFER_MAXSIZE);
				}
			}
			if (uart_data.data)
				free(uart_data.data);	
		}
	}
}

void uart_atcmds_init(void)
{
	OSA_STATUS status;

	LOG_PRINTF("%s[%d]: starting...\n", __FUNCTION__, __LINE__);

	status = OSATimerCreate(&uart_task_timer_ref);
	ASSERT(status == OS_SUCCESS);	

	status = OSAMsgQCreate(&uart_msgq_ref, "uart_msgq_ref", sizeof(uartMsgData_S), 300, OS_FIFO);
	ASSERT(status == OS_SUCCESS);

	uart_task_stack = malloc(UART_TASK_STACK_SIZE);
	ASSERT(uart_task_stack != NULL);

	status = OSATaskCreate(&uart_task_ref, uart_task_stack, UART_TASK_STACK_SIZE, 82, "uart_thread", uart_thread, NULL);
	ASSERT(status == OS_SUCCESS);	
}


